#include "Syntax/Parser.hpp"
#include "Syntax/Expression/AssignmentExpression.hpp"
#include "Syntax/Expression/BinaryExpression.hpp"
#include "Syntax/Expression/LiteralExpression.hpp"
#include "Syntax/Expression/NameExpression.hpp"
#include "Syntax/Expression/ParenthesizedExpression.hpp"
#include "Syntax/Expression/UnaryExpression.hpp"

#include "Syntax/Statement/BlockStatement.hpp"
#include "Syntax/Statement/ElseClause.hpp"
#include "Syntax/Statement/ForStatement.hpp"
#include "Syntax/Statement/IfStatement.hpp"
#include "Syntax/Statement/Statement.hpp"
#include "Syntax/Statement/VariableDeclaration.hpp"
#include "Syntax/Statement/WhileStatement.hpp"

#include <memory>

SharedExpression SyntaxParser::ParseExpression()
{
    return this->ParseAssignmentExpression();
}
SharedExpression SyntaxParser::ParseAssignmentExpression()
{
    if (this->Peek(0)->isMatch(SyntaxKind::Identifier) && (this->Peek(1)->isMatch(SyntaxKind::Assign)))
    {
        auto identifierToken = this->GetNextToken();
        auto operatorToken   = this->GetNextToken();
        auto right           = this->ParseAssignmentExpression();
        return std::make_shared<AssignmentExpressionSyntax>(identifierToken, operatorToken, right);
    }
    return this->ParseBinaryExpression();
}
SharedExpression SyntaxParser::ParseBinaryExpression(int parentPrecedence)
{
    SharedExpression left;
    auto             unaryOperatorPrecedence = this->GetUnaryOperatorPrecedence(this->Current()->GetKind());
    if (unaryOperatorPrecedence != 0 && unaryOperatorPrecedence >= parentPrecedence)
    {
        auto operatorToken = this->GetNextToken();
        auto operand       = ParseBinaryExpression(unaryOperatorPrecedence);
        left               = std::make_shared<UnaryExpressionSyntax>(operatorToken, operand);
    }
    else
    {
        left = this->ParsePrimaryExpression();
    }

    while (true)
    {
        auto precedence = this->GetBinaryOperatorPrecedence(this->Current()->GetKind());
        if (precedence == 0 || precedence <= parentPrecedence)
        {
            break;
        }
        auto operatorToken = this->GetNextToken();
        auto right         = this->ParseBinaryExpression(precedence);
        left               = std::make_shared<BinaryExpressionSyntax>(left, operatorToken, right);
    }
    return left;
}

SharedStatement SyntaxParser::ParseStatement()
{
    switch (this->Current()->GetKind())
    {
    case SyntaxKind::OpenBrace :
        return this->ParseBlackStatement();
    case SyntaxKind::Let :
    case SyntaxKind::Var :
        return this->ParseVariableDeclaration();
    case SyntaxKind::If :
        return this->ParseIfStatement();
    case SyntaxKind::While :
        return this->ParseWhileStatement();
    case SyntaxKind::For :
        return this->ParseForStatement();

    default :
        return this->ParseExpressionStatement();
    }
}
SharedStatement SyntaxParser::ParseBlackStatement()
{
    std::vector<SharedStatement> statements;

    auto openBrace = this->Match(SyntaxKind::OpenBrace);

    while (!this->Current()->isMatch({SyntaxKind::Eof, SyntaxKind::CloseBrace}))
    {
        auto startToken = this->Current();

        auto statement  = this->ParseStatement();
        statements.push_back(statement);

        // 如果当前的令牌出现错误，不被消耗,会陷入无限循环;
        // 这里增加判断，跳过当前令牌, 1.防止无限循环，2.尝试解析下一个表达式;
        if (startToken == this->Current())
            this->GetNextToken();
    }

    auto closeBrace = this->Match(SyntaxKind::CloseBrace);

    return std::make_shared<BlockStatementSyntax>(openBrace, statements, closeBrace);
}

SharedStatement SyntaxParser::ParseVariableDeclaration()
{
    auto expected    = this->Current()->isMatch(SyntaxKind::Let) ? SyntaxKind::Let : SyntaxKind::Var;

    auto keyword     = this->Match(expected);

    auto identifier  = this->Match(SyntaxKind::Identifier);
    auto equals      = this->Match(SyntaxKind::Assign);
    auto initializer = this->ParseExpression();

    return std::make_shared<VariableDeclarationSyntax>(keyword, identifier, equals, initializer);
}
SharedStatement SyntaxParser::ParseIfStatement()
{
    auto keyword    = this->Match(SyntaxKind::If);
    auto condition  = this->ParseExpression();
    auto statement  = this->ParseStatement();
    auto elseClause = this->ParseElseClause();
    return std::make_shared<IfStatementSyntax>(keyword, condition, statement, elseClause);
}
SharedElseClause SyntaxParser::ParseElseClause()
{
    if (this->Current()->GetKind() != SyntaxKind::Else)
        return nullptr;

    auto keyword   = this->Match(SyntaxKind::Else);
    auto statement = this->ParseStatement();

    return std::make_shared<ElseClauseSyntax>(keyword, statement);
}
SharedStatement SyntaxParser::ParseWhileStatement()
{
    auto keyword   = this->Match(SyntaxKind::While);
    auto condition = this->ParseExpression();
    auto body      = this->ParseStatement();

    return std::make_shared<WhileStatementSyntax>(keyword, condition, body);
}
SharedStatement SyntaxParser::ParseForStatement()
{
    auto keyword    = this->Match(SyntaxKind::For);
    auto identifier = this->Match(SyntaxKind::Identifier);
    auto equals     = this->Match(SyntaxKind::Assign);
    auto lowerBound = this->ParseExpression();
    auto toKeyword  = this->Match(SyntaxKind::To);
    auto upperBound = this->ParseExpression();

    auto body       = this->ParseStatement();
    return std::make_shared<ForStatementSyntax>(keyword, identifier, equals, lowerBound, toKeyword, upperBound, body);
}
SharedExpressionStatement SyntaxParser::ParseExpressionStatement()
{
    auto expression = this->ParseExpression();
    return std::make_shared<ExpressionStatementSyntax>(expression);
}
SharedExpression SyntaxParser::ParseTerm()
{
    auto left = this->ParseFactor();

    while (this->Current()->isMatch({SyntaxKind::Plus, SyntaxKind::Minus}))
    {
        auto operatorToken = this->GetNextToken();
        auto right         = this->ParseFactor();

        left               = std::make_shared<BinaryExpressionSyntax>(left, operatorToken, right);
    }
    return left;
}
SharedExpression SyntaxParser::ParseFactor()
{
    auto left = this->ParsePrimaryExpression();

    while (this->Current()->isMatch({SyntaxKind::Star, SyntaxKind::Slash, SyntaxKind::Mod}))
    {
        auto operatorToken = this->GetNextToken();

        auto right         = this->ParsePrimaryExpression();

        left               = std::make_shared<BinaryExpressionSyntax>(left, operatorToken, right);
    }
    return left;
}
SharedExpression SyntaxParser::ParsePrimaryExpression()
{
    auto curr = this->Current();

    switch (curr->GetKind())
    {
    case SyntaxKind::OpenParenthesis :
    {
        auto left       = this->GetNextToken();
        auto expression = this->ParseExpression();
        auto right      = this->Match(SyntaxKind::CloseParenthesis);
        return std::make_shared<ParenthesizedExpressionSyntax>(left, expression, right);
    };
    case SyntaxKind::Identifier :
    {
        auto identifierToken = this->GetNextToken();
        return std::make_shared<NameExpressionSyntax>(identifierToken);
    }
    case SyntaxKind::False :
    case SyntaxKind::True :
    {
        auto keywordToken = this->GetNextToken();
        // auto value = this->Current()->GetKind() == SyntaxKind::TRUE;
        // return std::make_shared<LiteralExpressionSyntax>(keywordToken,value);
        return std::make_shared<LiteralExpressionSyntax>(keywordToken);
    }
    case SyntaxKind::String :
    {
        auto keywordToken = this->GetNextToken();
        return std::make_shared<LiteralExpressionSyntax>(keywordToken);
    }
    default :
    {
        auto number = this->Match(SyntaxKind::Number);
        return std::make_shared<LiteralExpressionSyntax>(number);
    }
    }
}